/*----------------------------------------------------------------------------------*/
/*!
 * @file level.cxx
 * @brief Implementierung der Level-Routinen
 * @author steff
 *
 */
/*------------------------------------------------------------------------------------

------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "level.hxx"
#include "utils.hxx"

/*----------------------------------------------------------------------------------*/
/*!
 * @fn sli_verify_header(char *filename)
 *
 * @param filename the level file
 *
 * @result status, non-zero if failure
 *
 * @brief Verify the header of a level file.
 *
 */
/*----------------------------------------------------------------------------------*/
unsigned char sli_verify_header(char *filename)
{
	unsigned char ret = 1;
	struct sli_header header;
	FILE *levelfile;
	if ((levelfile = fopen(filename, "r")) != NULL)
	{
		if (fread(&header, sizeof(header), 1, levelfile) == 1)
		{
			if ((header.id[0] == 'S') && (header.id[1] == 'L') && (header.id[2] == 'I'))
			{
				ret = 0; /* woohoo, all tests successfully finished */
			}
			fclose(levelfile);
		} else
		{
			fclose(levelfile);
			ret = 2;
		}
	} else
	{
		ret = 1;
	}
	return ret;
}


/*----------------------------------------------------------------------------------*/
/*!
 * @fn sli_load_header(char *filename)
 *
 * @param filename the level file
 *
 * @result pointer to the loaded header if successful, otherwise NULL
 *
 * @brief Load the header of a level file.
 *
 */
/*----------------------------------------------------------------------------------*/
struct sli_header sli_load_header(char *filename)
{
	struct sli_header ret_header;
	FILE *levelfile;
	if (sli_verify_header(filename) == 0)
	{
		levelfile = fopen(filename, "r");
		fread(&ret_header, sizeof(ret_header), 1, levelfile);
#ifdef __BIG_ENDIAN__
		endianSwap(ret_header.dim_x);
		endianSwap(ret_header.dim_y);
#endif
		fclose(levelfile);
	} else
	{
		ret_header.version = 0;
	}
	return ret_header;
}


/*----------------------------------------------------------------------------------
	
	save a level

----------------------------------------------------------------------------------*/
unsigned int sli_save_level(char *filename, struct sli_header target_header, Scroll_LevelType *target_level)
{
//    struct sli_header temp_header = sli_load_header(filename);
    FILE * levelfile;
    levelfile = fopen(filename, "w");
#ifdef __BIG_ENDIAN__
		endianSwap(target_header.dim_x);
		endianSwap(target_header.dim_y);
#endif
    
    fwrite(&target_header, sizeof(target_header), 1, levelfile);
    
    #ifdef __BIG_ENDIAN__
		for(unsigned int i=0; i<(target_header.dim_x*target_header.dim_y);i++)
	    {
			endianSwap(target_level->level_bytearray[i]);
	    }
#endif
	fwrite(target_level->level_bytearray, sizeof(unsigned int), target_header.dim_x*target_header.dim_y, levelfile);
	fclose(levelfile);
}

/*----------------------------------------------------------------------------------*/
/*!
 * @fn sli_generate_level(char *target_filename, unsigned int dim_x, unsigned int dim_y)
 *
 * @param target_filename the target filename to write to
 * @param dim_x the width of the level in tiles
 * @param dim_y the height of the level in tiles
 *
 * @result status, 0 if ok, 1 if allocating mem failed
 *
 * @brief Generates SCRO-LV1 conform levels. VERY EXPERIMENTAL AND UGLY!
 *
 */
/*----------------------------------------------------------------------------------*/
unsigned char sli_generate_level(char *target_filename, unsigned int dim_x, unsigned int dim_y)
{
	struct sli_header header;
	FILE *target_handler;
	unsigned int *bytearray = (unsigned int*)malloc(dim_y*dim_x*sizeof(unsigned int));
	unsigned int *bytearray2 = (unsigned int*)malloc(dim_y*dim_x*sizeof(unsigned int));
	unsigned int counter = 0;
	unsigned char ret = 0;
	
	// if there was an error with memory allocation
	if ((bytearray != NULL) && (bytearray2 != NULL))
	{
		logger.log(0, "memory was successfully allocated: 2 x %d Bytes.", dim_x*dim_y*sizeof(unsigned int));
		while (counter < (dim_y*dim_x))
		{
			bytearray[counter] = 0;
			bytearray2[counter] = 0;
			counter++;
		}
		logger.log(0, "memory was successfully overwritten.");
		
		// the advanced level generator - begin
		
		// first pass - generate the land and the river
		
		srand((unsigned) time(NULL));
		//unsigned char riverWidth = (dim_x / 3) + rand() % (dim_x / 3);
		unsigned int riverWidth = 7;
		unsigned int riverWidthV = 0;
		unsigned int riverStart = (dim_x / 2) - (riverWidth / 2);
		unsigned int riverStartV = 0;
		counter = 0;
		unsigned int counter2 = 0;
		
		
		while(counter < dim_y)
		{
		    //riverWidthV = (dim_x / 3) + rand() % (dim_x / 3);
		    
		    
		    /*if (riverWidthV > riverWidth)
		    {
			riverWidth++;
		    } else if (riverWidthV < riverWidth)
		    {
			riverWidth--;
		    }*/
		    
		    if ((counter % 3) == 0)
		    {
			riverStartV = rand() % 3;
			if (riverStartV == 1)
			{
			    riverStart++;
			    if ((riverStart + riverWidth + 2) > dim_x) riverStart = dim_x - riverWidth - 2;
    			
			} else if (riverStartV == 2)
			{
			    riverStart--;
			    if (riverStart < 2) riverStart = 2;
			}
		    }
		    
		    counter2 = 0;
		    while(counter2 < dim_x)
		    {
		        if (counter2 < riverStart)
		        {
			    bytearray[counter*dim_x + counter2] = 0;
			} else if (counter2 > (riverStart + riverWidth))
			{
			    bytearray[counter*dim_x + counter2] = 0;
			} else
			{
			    bytearray[counter*dim_x + counter2] = 1;
		        }
			
			/* // uncomment if you want grass
			if (counter2 < (riverStart - 3))
			{
			    bytearray[counter*dim_x + counter2] = 18;
			} else if (counter2 > (riverStart + riverWidth + 3))
			{
			    bytearray[counter*dim_x + counter2] = 18;
			}*/
			
		        counter2++;
		    }
		    counter++;
		}
		
		// second pass - smooth landscape
		
		counter = 0;
		counter2 = 0;
		
		unsigned int tempChar = 0;
		
		while(counter < dim_y)
		{
		    counter2 = 0;
		    tempChar = bytearray[counter*dim_x];
		    while(counter2 < dim_x)
		    {
			if (counter2 > 0)
			{
			    if ((tempChar == 0) && (bytearray[counter*dim_x + counter2] == 1))
			    {
				if (((counter + 1) < dim_y) && ((counter2 + 1) < dim_x) && (counter > 0) && (counter2 > 0))
				{
				    if ((bytearray[(counter+1)*dim_x + counter2] == 0))
				    {
					bytearray[counter*dim_x + counter2] = 7;
				    } else if ((bytearray[(counter-1)*dim_x + counter2-1] == 7))
				    {
					bytearray[counter*dim_x + counter2] = 8;
				    } else if ((bytearray[(counter-1)*dim_x + counter2] == 0))
				    {
					bytearray[counter*dim_x + counter2] = 5;
				    } else if (((bytearray[(counter+1)*dim_x + counter2] == 1)) && ((bytearray[(counter+1)*dim_x + counter2-1] == 1)))
				    {
					bytearray[counter*dim_x + counter2] = 4;
				    }
				}
				if (bytearray[counter*dim_x + counter2] == 1) bytearray[counter*dim_x + counter2] = 6;
			    } else if ((tempChar == 1) && (bytearray[counter*dim_x + counter2] == 0))
			    {
				if (((counter + 1) < dim_y) && ((counter2 + 1) < dim_x) && (counter > 0) && (counter2 > 0))
				{
				    if ((bytearray[(counter+1)*dim_x + counter2] == 1))
				    {
					bytearray[counter*dim_x + counter2] = 16;
				    } else if ((bytearray[(counter-1)*dim_x+counter2-1] == 16))
				    {
					bytearray[counter*dim_x + counter2] = 15;
				    } else if ((bytearray[(counter-1)*dim_x+counter2] == 1))
				    {
					bytearray[counter*dim_x + counter2] = 12;
				    } else if (((bytearray[(counter+1)*dim_x + counter2] == 0)) && ((bytearray[(counter+1)*dim_x + counter2-1] == 0)))
				    {
					bytearray[counter*dim_x + counter2] = 13;
				    }
				}
				if (bytearray[counter*dim_x + counter2] == 0) bytearray[counter*dim_x + counter2] = 14;
			    }
			}
			tempChar = bytearray[counter*dim_x + counter2];
			counter2++;
		    }
		    counter++;
		}
		
		
		
		// the advanced level generator - end
		
		
		// fill header
		header.id[0] = 'S';
		header.id[1] = 'L';
		header.id[2] = 'I';
		header.id[3] = 0;
		header.id[4] = 0;
		header.version = SCRO_LEVEL_INFORMATION_VERSION;
		sprintf(header.author, "%s!", getenv("USERNAME"));
		sprintf(header.title, "%s's level", getenv("USERNAME"));
		sprintf(header.tileset, "default");
		sprintf(header.enemy_map, "\0");
		
		header.dim_x = dim_x;
		header.dim_y = dim_y;
		
#ifdef __BIG_ENDIAN__
		endianSwap(header.dim_x);
		endianSwap(header.dim_y);
#endif	
		
		/* open target file */
		target_handler = fopen(target_filename, "w");
		/* write handler */
		fwrite(&header, sizeof(header), 1, target_handler);

		logger.log(0, "will write file now...");
#ifdef __BIG_ENDIAN__
		for(unsigned int i=0; i<(dim_x*dim_y);i++)
	    {
			endianSwap(bytearray[i]);
	    }
#endif
		fwrite(bytearray, sizeof(unsigned int), dim_x*dim_y, target_handler);
		//fwrite(bytearray2, sizeof(unsigned int), dim_x*dim_y, target_handler);
		logger.log(0, "data written, preparing quit...");
		fclose(target_handler);
		logger.log(0, "file closed, now freeing allocated memory...");
		free(bytearray);
		logger.log(0, "memory free'd, ready.");
		ret = 0;
	} else
	{
		logger.log(2, "wasn't able to allocate memory.");
		ret = 1;
	}
	return ret;
}

/*----------------------------------------------------------------------------------*/
/*!
 * @fn sli_check_file(char *filename)
 *
 * @param filename the level file
 *
 * @result status, non-zero if invalid level file
 *
 * @brief Checks a file.
 *
 */
/*----------------------------------------------------------------------------------*/
unsigned char sli_check_file(char *filename)
{
	unsigned char ret = 1;
	struct sli_header header;
	header = sli_load_header(filename);
	if (header.version != 0)
	{
		/* FIX: need more checks, eg filesize, if tilesets exists, and so on */
		ret = 0;
	}
	return ret;
}

// free level struct
unsigned int sli_free_level(Scroll_LevelType * free_level)
{
	SDL_Surface *temp;
	unsigned int array_size = 0;
	// determin array size...
	temp = IMG_Load("data/graphics/leveltiles.png");
	array_size = temp->w / 32;
//	for(int i = 0; i < array_size; i++) SDL_FreeSurface(free_level->tile_surface[i]);
	SDL_FreeSurface(temp);
	
	for(int i = 0; i < array_size; i++)
	{
		SDL_FreeSurface(free_level->tiles_array[i]);
	}
	
	//free(free_level->tiles_array);
	//free(free_level->tile_surface);
	
	return 1;
}


/* return leveltype struct */
Scroll_LevelType * sli_return_level(char *filename)
{
	// return value
	Scroll_LevelType *returnLevelType = new Scroll_LevelType;
	// load header
	struct sli_header header_temp = sli_load_header(filename);
	if (header_temp.version > 0)
	{
		// load body
		unsigned int * bytearray = sli_load_body(filename);
		// tiles surface
		SDL_Surface * temp;
		SDL_Surface * tiles;
		SDL_Rect source_rect;
		//
		unsigned int array_size = 0;	
		// 
		unsigned int counter = 0;

		// fill return structSDL_CreateRGBSurface(SDL_SWSURFACE, XMAX, 32, VIDEO_BITS, 0, 0, 0, 0);
		returnLevelType->start_x_t = 0;
		returnLevelType->start_y_t = header_temp.dim_y - 21;
		returnLevelType->level_width_t = header_temp.dim_x;
		returnLevelType->level_height_t = header_temp.dim_y;
		returnLevelType->tile_width = 32;
		returnLevelType->tile_height = 32;

		// TODO count needed amount of tiles
		// WORKAROUND load ALL tiles
		
		// TODO load tiles file which is actually defined by level header
		temp = IMG_Load("data/graphics/leveltiles.png");
		tiles = SDL_DisplayFormat(temp);
		array_size = tiles->w / 32;
		returnLevelType->tiles_array_size = array_size;
		
		// TODO create SDL Surface array
		returnLevelType->tiles_array = new SDL_Surface * [array_size];
		source_rect.y = 0;
		source_rect.w = 32;
		source_rect.h = 32;
		while(counter < array_size)
		{
			returnLevelType->tiles_array[counter] = SDL_CreateRGBSurface(SDL_HWSURFACE, 32, 32, VIDEO_BITS, 0, 0, 0, 0);
			source_rect.x = counter * 32;
			//if (source_rect.x > tiles->w) source_rect.x = 0; // 0 is fallback
			SDL_BlitSurface(tiles, &source_rect, returnLevelType->tiles_array[counter], NULL);
			counter++;
		}
		counter = 0;
		SDL_Surface *** tile_all = new SDL_Surface ** [header_temp.dim_y];
		while(counter < header_temp.dim_y)
		{
			//logger.log(0, "counter: %d", counter);
			tile_all[counter] = new SDL_Surface * [header_temp.dim_x];
			for(unsigned int i = 0; i<header_temp.dim_x; i++)
			{
				//logger.log(0, "i: %d, ba: %d", i,bytearray[counter*header_temp.dim_x+i]);
				tile_all[counter][i] = returnLevelType->tiles_array[bytearray[counter*header_temp.dim_x+i]];
			}
			counter++;
		}
		counter = 0;
	
		returnLevelType->tile_surface = tile_all;
		returnLevelType->level_bytearray = sli_load_body(filename);
	
		// TODO Free vars 
		SDL_FreeSurface(temp);
		SDL_FreeSurface(tiles);
		free(bytearray);
	} else
	{
		returnLevelType = NULL;
	}
	return returnLevelType;
}


/*----------------------------------------------------------------------------------*/
/*!
 * @fn sli_load_body(char *filename, unsigned int *target_array)
 *
 * @param filename the level file
 *
 * @result a pointer to the 'content' of the level
 *
 * @brief Load the body of a level file.
 *
 */
/*----------------------------------------------------------------------------------*/
unsigned int * sli_load_body(char *filename/*, unsigned int *target_array*/)
{
	FILE *levelfile;
	struct sli_header header;
	size_t result;
	unsigned int * bytearray;
		
	if (sli_check_file(filename) == 0)
	{
		/* great, file is valid */
		logger.log(0, "reading header...");
		header = sli_load_header(filename);
		levelfile = fopen(filename, "r");

		logger.log(0, "allocating mem...");
		bytearray = (unsigned int*)malloc(header.dim_y*header.dim_x*sizeof(unsigned int));
		if (bytearray == NULL)
		{
			fclose(levelfile);
			logger.log(2, "wasn't able to allocate memory");
			return NULL;
		}
		
		logger.log(0, "reading body...");
		fseek(levelfile, 128, SEEK_SET);
		result = fread(bytearray, sizeof(unsigned int), header.dim_x*header.dim_y, levelfile);
#ifdef __BIG_ENDIAN__	
		for(unsigned int i=0; i<(header.dim_x*header.dim_y);i++)
		{
			endianSwap(bytearray[i]);
		}
#endif			
		logger.log(0, "%d elements read, expected %d elements...", (int)result, header.dim_x*header.dim_y);

		if (result != (header.dim_x*header.dim_y)) 
		{ 
			fclose(levelfile);
			logger.log(2, "FAILURE, exiting!");
			return NULL;
		}
		
		logger.log(0, "level body successfully loaded!");
		fclose(levelfile);
	}
	return bytearray;
}

